#ifndef GRID_H
#define GRID_H

/*!
 * \file      grid.h
 * \brief     Container for Square and Shape.
 *
 *            This is where the action of the game will take place.
 *            It contain the Squares of the Shapes that it's absorded
 *            and the current Shape that the player control.
 *
 * \author    Duriez Franck
 * \author    Carton Paul-Henri
 * \version   0.2
 */

#include <vector>
#include <iostream>

#include "tetrisexception.h"
#include "drawable.h"
#include "square.h"
#include "container.h"
#include "shape.h"
#include "action.h"

class Shape;

/*!
 * \class Grid
 * \brief Container for Square and Shape.
 *
 *        This is where the action of the game will take place.
 *        It contain the Squares of the Shapes that it's absorded
 *        and the current Shape that the player control.
 * \todo Implement a position system.
 * \todo Abstract the class.
 */
class Grid : public SquareContainer /*{{{*/
{
	Q_OBJECT
/*{{{*/
public:
	/*!
	 * \brief Constructor.
	 * \param displayWidget : displayer
	 * \param parent        : parent of the Grid
	 * \param dt            : pointer to the infinitesimal part of time
	 */
	Grid(QSFMLWidget* displayWidget, QObject* parent, float* dt);

	/*!
	 * \brief Destructor.
	 */
	virtual ~Grid();

	/*!
	 * \brief Set the background at the rigth position.
	 * \todo "Delete" this useless function.
	 */
	void setBackground();

signals:
	/*!
	 * \brief Declare that lines have been destroyed.
	 * \param lines : the current number of lines in destruction
	 */
	void linesDestroyed(int lines);

	/*!
	 * \brief Declare that the play lose !
	 */
	void gameOver();

private:
	/*!
	 * \brief Move the row i down from line j until the end.
	 * \param coord : Coordinate where a Square is dead
	 *
	 * This function is used to step down the part of a row which where over
	 * a Square in destruction.
	 */
	void moveRowDown(Vector2i coord);

	/*!
	 * \brief Inform if the line is full or not.
	 * \param line : Index of the line to test
	 */
	bool isLineFull(int line) const throw();

	/*!
	 * \brief Inform if exists full line.
	 */
	bool isALineFull() const throw();

	sf::Shape m_background; ///< Background of the grid
/*}}}*/

	/************
	 * DRAWABLE *
	 ************/
/*{{{*/
public:
	void update();
	void draw() const;
	std::string toString() const;
/*}}}*/

	/***********
	 * ACTIONS *
	 ***********/
/*{{{*/
public:
	/*!
	 * \brief Action which destroy full lines
	 *
	 * For the moment it's not really justified to
	 * implement destroyFullLines as an Action but
	 * we could imagine to set some special effects
	 * during the destruction in addition to the
	 * Square's effects, something like a laser beam
	 * travelling line before the Square destruction.
	 */
	Action<Grid> destroyFullLines;

private:
	void destroyFullLinesStartingProcess();
	void destroyFullLinesProcess();
	void destroyFullLinesStoppingProcess();
/*}}}*/

	/*********
	 * SHAPE *
	 *********/
/*{{{*/
public:
	/*!
	 * \brief Put a new Shape in the Grid.
	 * \param ashape : Shape to put in the Grid
	 */
	void setShape(Shape* ashape) throw(TetrisException::UnexpectedShape,
	                                   TetrisException::ShapeOwnershipConflict);

	/*!
	 * \brief Return the pointer of the current Shape in the Grid.
	 */
	Shape* getShape() const;

public slots:
	/*!
	 * \brief Move the current Shape Down.
	 *
	 * This function try to move the Shape down, if it succeeds she
	 * return true, otherwise false. If it fails, that means that it
	 * is either because there are no Shape, or because the Shape is
	 * already moving down, or because the Shape has been absorbed by
	 * the Grid.
	 */
	bool moveTheShapeDown();

signals:
	/*!
	 * \brief Declare that the current Shape has been absorbed.
	 */
	void shapeAbsorbed();

private:
	Shape* m_shape; ///< Current Shape in the Grid
/*}}}*/

	/***********
	 * SQUARES *
	 ***********/
/*{{{*/
private:
	/*!
	 * \brief Used to absorb the current Shape.
	 */
	void transfertSquareFromShapeToGrid() throw(TetrisException::ExpectedShape, TetrisException::UnexpectedSquare);

	/*!
	 * \brief Give the good format to a Square to be in the Grid.
	 * \param coord : Coordinate of the Square to format
	 *
	 * This function is used when there is a Square transfert between two SquareContainer
	 * to give the transfered Square, the appropriate format for the receiver SquareContainer.
	 */
	void format(Vector2i coord) const throw(TetrisException::ExpectedSquare);

	/*!
	 * \brief Declare that the Square at coord is destroyed.
	 * \param coord : Coordinate of the Square to format
	 *
	 * This function is called by a Square contained in the Grid to signal that it's dead.
	 * So the grid can clean the address at coord and move the row down.
	 * Remark that this function is virtual and private in SquareContainer and that the
	 * only friend of SquareContainer is Square, so it can't be hijack.
	 */
	void declareDeadObject(Vector2i coord);

	int m_square_in_destruction_number; ///< The current number of Square in destruction
/*}}}*/
}; /*}}}*/

#endif // GRID_H
